﻿using HandmadeShop.BusinessLogic.Entities;
using HandmadeShop.BusinessLogic.Repositories;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Transactions;
using Db = HandmadeShop.Database;

namespace HandmadeShop.BusinessLogic
{
    public class ProductBL : BaseBL
    {
        private Guid currentUserId;
        private GenericRepository<Db.Product> productRepository;
        private GenericRepository<Db.ProductType> productTypeRepository;
        private GenericRepository<Db.ProductPrice> productPriceRepository;

        public ProductBL()
            : this(null, Guid.Empty)
        {

        }

        public ProductBL(Guid currentUserId)
            : this(null, currentUserId)
        {
            
        }

        public ProductBL(Db.HandmadeShopEntities context, Guid currentUserId)
            : base(context)
        {
            this.currentUserId = currentUserId;
            this.Init();
        }

        private void Init()
        {
            this.productRepository = this.unitOfWork.ProductRepository;
            this.productTypeRepository = this.unitOfWork.ProductTypeRepository;
            this.productPriceRepository = this.unitOfWork.ProductPriceRepository;
        }

        /// <summary>
        /// Check code or name is dupplicate
        /// (0 = success, 1 = code, 2 = name)
        /// </summary>
        /// <param name="code"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private int IsCodeNameExist(string code, string name)
        {
            var list = this.productRepository.Get(p => p.Code.Equals(code) || p.Name.Equals(name));

            if (list != null)
            {
                foreach (var item in list)
                {
                    if (item != null)
                    {
                        if (item.Code.Equals(code))
                            return 1;
                        else
                            return 2;
                    }
                }
            }

            return 0;
        }

        /// <summary>
        /// Check code or name type is dupplicate
        /// (0 = success, 1 = code, 2 = name)
        /// </summary>
        /// <param name="code"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private int IsCodeNameTypeExist(string code, string name)
        {
            var list = this.productTypeRepository.Get(p => p.Code.Equals(code) || p.Name.Equals(name));

            if (list != null)
            {
                foreach (var item in list)
                {
                    if (item != null)
                    {
                        if (item.Code.Equals(code))
                            return 1;
                        else
                            return 2;
                    }
                }
            }

            return 0;
        }

        /// <summary>
        /// Create product.
        /// (0 = success, 1 = null object, 2 = exist code, 3 = exist name)
        /// </summary>
        /// <param name="product"></param>
        /// <returns></returns>
        public int CreateProduct(Entities.Product product)
        {
            if (product == null) return 1;

            int statusCodeName = this.IsCodeNameExist(product.Code, product.Name);
            if (statusCodeName == 1)
                return 2;
            if (statusCodeName == 2)
                return 3;

            
            using (TransactionScope transaction = new TransactionScope())
            {
                // Get type
                Db.ProductType entityProductType = null;
                Db.ProductPrice entityProductPrice = null;

                if (product.ProductType.Id.Equals(Guid.Empty))
                {
                    statusCodeName = this.IsCodeNameTypeExist(product.ProductType.Code, product.ProductType.Name);
                    if (statusCodeName == 1)
                        return 4;
                    if (statusCodeName == 2)
                        return 5;

                    entityProductType = this.CreateProductType(product.ProductType);
                }
                else
                {
                    entityProductType = new Db.ProductType();
                    entityProductType.Id = product.ProductType.Id;
                }

                if (product.ProductPrice != null)
                {
                    entityProductPrice = this.CreateProductPrice(product.ProductPrice);                    
                }
                
                var now = DateTime.Now;
                var entity = new Db.Product()
                {
                    Id = Guid.NewGuid(),
                    Name = product.Name,
                    Code = product.Code,
                    Description = product.Description,
                    Instruction = product.Instruction,
                    Image1 = product.Image1,
                    Image2 = product.Image2,
                    Image3 = product.Image3,
                    Image4 = product.Image4,
                    Image5 = product.Image5,
                    CreatedBy = this.currentUserId,
                    CreatedDate = now,
                    ModifiedBy = this.currentUserId,
                    ModifiedDate = now,
                    ProductTypeId = entityProductType.Id
                };
                entity.ProductPrices.Add(entityProductPrice);

                this.productRepository.Insert(entity);

                this.productTypeRepository.Save();
                this.productRepository.Save();
                this.productPriceRepository.Save();

                transaction.Complete();

                return 0;
            }
        }

        public Db.ProductType CreateProductType(ProductType productType)
        {
            if (productType == null) return null;

            var now = DateTime.Now;
            var entity = new Db.ProductType()
            {
                Id = Guid.NewGuid(),
                Name = productType.Name,
                Code = productType.Code,                     
                CreatedBy = this.currentUserId,
                CreatedDate = now,
                ModifiedBy = this.currentUserId,
                ModifiedDate = now
            };

            this.productTypeRepository.Insert(entity);

            return entity;
        }

        public Db.ProductPrice CreateProductPrice(ProductPrice productPrice)
        {
            if (productPrice == null) return null;

            var now = DateTime.Now;
            var entity = new Db.ProductPrice()
            {
                Id = Guid.NewGuid(),
                ProductId = productPrice.Id,
                Price = productPrice.Price,
                CreatedBy = this.currentUserId,
                CreatedDate = now
            };

            this.productPriceRepository.Insert(entity);

            return entity;
        }

        public IList<Product> GetNewProducts()
        {
            var list = this.productRepository.Get(orderBy: p => p.OrderByDescending(q => q.CreatedDate)).Take(4);

            IList<Product> rs = new List<Product>();

            foreach (var item in list)
            {
                var tmp = this.ConvertToProduct(item);
                if (tmp == null)
                    continue;

                rs.Add(tmp);
            }

            return rs;
        }

        public Guid GetTypeByCode(string code)
        {
            if (string.IsNullOrEmpty(code)) return Guid.Empty;

            var type = this.productTypeRepository.Get(p => p.Code.Equals(code)).FirstOrDefault();
            if (type == null) return Guid.Empty;

            return type.Id;
        }

        public IList<Product> GetProductsByType(Guid id)
        {
            if (id == Guid.Empty) return null;

            var list = this.productRepository.Get(p => p.ProductTypeId == id);

            IList<Product> rs = new List<Product>();

            foreach (var item in list)
            {
                var tmp = this.ConvertToProduct(item);
                if (tmp == null)
                    continue;

                rs.Add(tmp);
            }

            return rs;
        }

        public IList<Product> GetHotProducts()
        {
            // Base on orders of customer
            // In beta version: base on random number

            var list = this.productRepository.Get(orderBy: p => p.OrderBy(q => System.Data.Objects.SqlClient.SqlFunctions.Rand()),
                includeProperties: "ProductPrices").Take(4);

            IList<Product> rs = new List<Product>();

            foreach (var item in list)
            {
                var tmp = this.ConvertToProduct(item);
                if (tmp == null)
                    continue;

                rs.Add(tmp);
            }

            return rs;
        }

        public IList<ProductType> GetAllProductType()
        {
            var list = this.productTypeRepository.Get();
            IList<ProductType> rs = new List<ProductType>();

            foreach (var item in list)
            {
                ProductType pt = new ProductType
                {
                    Id = item.Id,
                    Name = item.Name,
                    Code = item.Code
                };

                rs.Add(pt);
            }

            return rs;
        }

        private Product ConvertToProduct(Db.Product dbProduct)
        {
            if (dbProduct == null) return null;

            var rs = new Product
            {
                Id = dbProduct.Id,
                Code = dbProduct.Code,
                Name = dbProduct.Name,
                Image1 = dbProduct.Image1,
                Image2 = dbProduct.Image2,
                Image3 = dbProduct.Image3,
                Image4 = dbProduct.Image4,
                Image5 = dbProduct.Image5,
                Description = dbProduct.Description,
                Instruction = dbProduct.Instruction
            };

            if (dbProduct.ProductType != null)
            {
                rs.ProductType = new ProductType
                {
                    Id = dbProduct.ProductType.Id,
                    Code = dbProduct.ProductType.Code,
                    Name = dbProduct.ProductType.Name
                };
            }

            if (dbProduct.ProductPrices != null && dbProduct.ProductPrices.Count > 0)
            {
                var prices = dbProduct.ProductPrices.ToList();
                rs.ProductPrice = new ProductPrice
                {
                    Id = prices[0].Id,
                    Price = prices[0].Price
                };
            }

            return rs;
        }
    }
}
